﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace SCOSub.FFMS2
{
    class FFMS2VideoProvider : IDisposable
    {
        #region 变量
        VideoProperties videoproperties;
        IntPtr index = IntPtr.Zero;
        IntPtr videobase;
        AVFrameLite framecache;
        int currentframe;
        FrameBuffer fb;
        StringBuilder errs;
        BitmapData bitmapData;
        Rectangle framedataRectangle;
        string indexfile;
        List<int> keyframeslist;
        List<double> timecode;
        #endregion
        #region 属性
        public double[] TimeCode
        {
            get
            {
                return timecode.ToArray();
            }
        }
        public int[] KeyFrameList
        {
            get
            {
                return keyframeslist.ToArray();
            }
        }

        public int Currentframe
        {
            get { return currentframe; }
        }
        public VideoProperties Videoproperties
        {
            get { return videoproperties; }
        }
        #endregion
        #region 构造函数
        public FFMS2VideoProvider(string filename)
            : this(filename, null)
        {
        }
        public FFMS2VideoProvider(string filename, FFMS2API.IndexCallbackDelegate icd)
        {
            errs = new StringBuilder(1000);
            Load(filename, icd, Const.FFMS2VideoProviderFrameBuffer);
        }
        public FFMS2VideoProvider(string filename, int framecacheCount)
        {
            errs = new StringBuilder(1000);
            Load(filename, null, framecacheCount);
        }
        #endregion
        #region 公有方法
        public Bitmap GetFrameBitmap()
        {
            return GetFrameBitmap(currentframe);
        }
        public Bitmap GetFrameBitmap(int n)
        {
            if (videobase == null)
                throw new Exception("GetFrameBitmap需要的videobase为null");
            if (n >= videoproperties.NumFrames)
                throw new Exception("指定的帧数超过最大值");
            Bitmap b = fb.Find(n);

            if (b != null)
            {
                return b;
            }
            else
            {
                IntPtr pAV = FFMS2API.GetFrame(videobase, n, errs, (uint)errs.Capacity);
                if (pAV == IntPtr.Zero)
                    return null;
                framecache = (AVFrameLite)Marshal.PtrToStructure(pAV, typeof(AVFrameLite));

                bitmapData.Width = videoproperties.Width;
                bitmapData.Height = videoproperties.Height;
                bitmapData.PixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
                bitmapData.Stride = framecache.Linesize0;
                bitmapData.Scan0 = framecache.Data0;

                fb.Enqueue(n, ref b);

                b.LockBits(framedataRectangle, ImageLockMode.WriteOnly | ImageLockMode.UserInputBuffer, System.Drawing.Imaging.PixelFormat.Format24bppRgb, bitmapData);
                b.UnlockBits(bitmapData);
            }
            currentframe = n;
            return b;


        }
        public int GetNextClosestKeyFrameIndex()
        {
            int _index = keyframeslist.BinarySearch(currentframe);
            if (_index >= 0)
            {
                ++_index;
                if (_index >= keyframeslist.Count)
                    return videoproperties.NumFrames - 1;
                else
                    return keyframeslist[_index];
            }
            else
            {
                _index = ~_index;
                if (_index >= keyframeslist.Count)
                    return videoproperties.NumFrames - 1;
                else
                    return keyframeslist[_index];
            }

        }
        public int GetPrevClosestKeyFrameIndex()
        {
            int _index = keyframeslist.BinarySearch(currentframe);
            

            if (_index >= 0)
            {
                --_index;
                if (_index < 0)
                    return 0;
                else
                    return keyframeslist[_index];
            }
            else
            {
                _index = ~_index;
                _index -= 1;
                if (_index < 0)
                    return 0;
                else
                    return keyframeslist[_index];
            }

        }
        #endregion
        #region 私有方法
        private void Load(string filename, FFMS2API.IndexCallbackDelegate icd,int framecacheCount)
        {
            if (File.Exists(filename) == false)
                throw new Exception("文件不存在");
            FFMS2API.Init();
            index = IntPtr.Zero;


            indexfile = Path.GetTempPath() + encodeFilename(Path.GetFileNameWithoutExtension(filename)) + Path.GetExtension(filename) + ".ffms2index";
            if (File.Exists(indexfile))
            {
                IntPtr pindex = FFMS2API.ReadIndex(indexfile, errs, (uint)errs.Capacity);
                if (pindex == IntPtr.Zero)
                {
                    //make index
                    pindex = FFMS2API.MakeIndex(filename, 0, 0, null, true, icd, IntPtr.Zero, errs, (uint)errs.Capacity);
                    if (pindex == IntPtr.Zero)
                        throw new Exception("MakeIndex出错，无法创建索引\n" + errs.ToString());
                    FFMS2API.WriteIndex(indexfile, pindex, errs, (uint)errs.Capacity);
                }
                index = pindex;
            }
            else
            {
                //make index and write index
                index = FFMS2API.MakeIndex(filename, 0, 0, null, true, icd, IntPtr.Zero, errs, (uint)errs.Capacity);
                if (index == IntPtr.Zero)
                    throw new Exception("MakeIndex出错，无法创建索引\n" + errs.ToString());
                FFMS2API.WriteIndex(indexfile, index, errs, (uint)errs.Capacity);
            }

            int firstVideoTrack = FFMS2API.GetFirstTrackOfType(index, TrackType.FFMS_TYPE_VIDEO, errs, (uint)errs.Capacity);
            if (firstVideoTrack == -1)
                throw new Exception("GetFirstTrackOfType出错\n" + errs.ToString());
            videobase = FFMS2API.CreateVideoSource(filename, firstVideoTrack, index, string.Empty, Environment.ProcessorCount, 1, errs, (uint)errs.Capacity);
            if (videobase == IntPtr.Zero)
                throw new Exception("CreateVideoSource出错\n" + errs.ToString());
            IntPtr pVP = FFMS2API.GetVideoProperties(videobase);
            if (pVP == IntPtr.Zero)
                throw new Exception("GetVideoProperties出错\n" + errs.ToString());
            videoproperties = (VideoProperties)Marshal.PtrToStructure(pVP, typeof(VideoProperties));
            int result = FFMS2API.SetOutputFormat(videobase, PixelFormat.FFMS_PIX_FMT_BGR24, videoproperties.Width, videoproperties.Height, errs, (uint)errs.Capacity);
            if (result != 0)
                throw new Exception("SetOutputFormat出错\n" + errs.ToString());

            fb = new FrameBuffer(framecacheCount, videoproperties.Width, videoproperties.Height);
            
            bitmapData = new BitmapData();
            framedataRectangle = new Rectangle(0, 0, videoproperties.Width, videoproperties.Height);

            getKeyFramesList();
            getTimeCode();
        }
        private void getKeyFramesList()
        {
            FileStream fs = new FileStream(indexfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            BinaryReader br = new BinaryReader(fs);
            keyframeslist = new List<int>();
            br.BaseStream.Seek(60, SeekOrigin.Begin);
           
            bool iskey = false;

            for (int i = 0; i < videoproperties.NumFrames; i++)
            {
                iskey = br.ReadBoolean();
                br.BaseStream.Seek(31, SeekOrigin.Current);
                if (iskey)
                    keyframeslist.Add(i);
            }
            br.Close();
            fs.Dispose();
        }
        private void getTimeCode()
        {
            try
            {

                string tempfilename = Path.GetTempFileName();

                int trackNumber = FFMS2API.GetFirstTrackOfType(index, TrackType.FFMS_TYPE_VIDEO, errs, (uint)errs.Capacity);
                if (trackNumber == -1)
                    throw new Exception("查找轨道出错");
                IntPtr pTItrackindex = FFMS2API.GetTITrackIndex(index, trackNumber, errs, (uint)errs.Capacity);
                if (pTItrackindex == IntPtr.Zero)
                    throw new Exception("GetTITrackIndex出错");
                int result = FFMS2API.WriteTimecodes(pTItrackindex, tempfilename, errs, (uint)errs.Capacity);
                if (result != 0)
                    throw new Exception("WriteTimecodes出错");

                timecode = new List<double>();
                double temp;
                StreamReader sr;

                sr = new StreamReader(tempfilename);
                if (sr.ReadLine().Trim().ToLower() != "# timecode format v2")
                    throw new Exception();
                while (sr.Peek() != -1)
                {
                    temp = 0;
                    double.TryParse(sr.ReadLine().Trim(), out temp);
                    timecode.Add(temp);
                }

            }
            catch
            {
                timecode = null;
            }



        }
        /// <summary>
        /// 为了解决对unicode文件名的支持
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <returns></returns>
        private string encodeFilename(string filename)
        {
            byte[] _filenamebyte = Encoding.Unicode.GetBytes(filename);
            StringBuilder sb = new StringBuilder();
            foreach (byte b in _filenamebyte)
            {
                sb.Append(b.ToString("x"));
            }
            _filenamebyte = null;
            return sb.ToString();
        }
        #endregion
        #region IDisposable 成员

        public void Dispose()
        {
            if (index != IntPtr.Zero)
                FFMS2API.DestroyFrameIndex(index);
            if (videobase != IntPtr.Zero)
                FFMS2API.DestroyVideoSource(videobase);
            fb.Dispose();

        }

        #endregion
    }
    sealed class FFMS2API
    {
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate int IndexCallbackDelegate(int State, long Current, long Total, System.IntPtr Private);
        /// <summary>
        /// 初始化FFMS
        /// </summary>
        [DllImport("ffms2.dll", EntryPoint = "_FFMS_Init@0", CallingConvention = CallingConvention.StdCall)]
        public static extern void Init();

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_ClosestFrameFromDTS@12", CallingConvention = CallingConvention.StdCall)]
        public static extern int ClosestFrameFromDTS(IntPtr pFIV, long DTS);


        [DllImport("ffms2.dll", EntryPoint = "_FFMS_CreateAudioSource@20", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr CreateAudioSource([In(), MarshalAs(UnmanagedType.LPStr)] string SourceFile, int Track, IntPtr pTrackIndices, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);//return *aideobase

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_CreateVideoSource@32", CallingConvention = CallingConvention.StdCall)]
        public static extern System.IntPtr CreateVideoSource([In(), MarshalAs(UnmanagedType.LPStr)]
            string SourceFile, int Track, IntPtr pTrackIndices, [In(), MarshalAs(UnmanagedType.LPStr)]
            string PP, int Threads, int SeekMode, [MarshalAs(UnmanagedType.LPStr)]
            StringBuilder ErrorMsg, uint MsgSize);                  //return *videobase

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_DestroyAudioSource@4", CallingConvention = CallingConvention.StdCall)]
        public static extern void DestroyAudioSource(IntPtr pAudioBase);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_DestroyFrameIndex@4", CallingConvention = CallingConvention.StdCall)]
        public static extern void DestroyFrameIndex(IntPtr pFrameindex);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_DestroyVideoSource@4", CallingConvention = CallingConvention.StdCall)]
        public static extern void DestroyVideoSource(IntPtr pVideoBase);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_FindClosestKeyFrame@16", CallingConvention = CallingConvention.StdCall)]
        public static extern int FindClosestKeyFrame(IntPtr pFIV, int Frame, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_FrameFromDTS@12", CallingConvention = CallingConvention.StdCall)]
        public static extern int FrameFromDTS(IntPtr pFIV, long DTS);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetAudio@32", CallingConvention = CallingConvention.StdCall)]
        public static extern int GetAudio(IntPtr pAudioBase, IntPtr Buf, long Start, long Count, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetAudioProperties@4", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetAudioProperties(IntPtr pAudioBase);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetFirstTrackOfType@16", CallingConvention = CallingConvention.StdCall)]
        public static extern int GetFirstTrackOfType(IntPtr TrackIndices, TrackType TrackType, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetFrame@16", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetFrame(IntPtr pVideoBase, int n, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetFrameByTime@20", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetFrameByTime(IntPtr pVideoBase, double Time, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetFrameInfo@16", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetFrameInfo(IntPtr pFIV, int Frame, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetNumFrames@4", CallingConvention = CallingConvention.StdCall)]
        public static extern int GetNumFrames(IntPtr pFIV);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetNumTracks@4", CallingConvention = CallingConvention.StdCall)]
        public static extern int GetNumTracks(IntPtr pTrackIndices);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetTITrackIndex@16", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetTITrackIndex(IntPtr pTrackIndices, int Track, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize); //return *FIV

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetTimeBase@4", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetTimeBase(IntPtr pFIV);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetTrackType@4", CallingConvention = CallingConvention.StdCall)]
        public static extern int GetTrackType(IntPtr pFIV);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetVSTrackIndex@4", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetVSTrackIndex(IntPtr pVideoBase);//return *FIV

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_GetVideoProperties@4", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetVideoProperties(IntPtr pVideoBase);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_MakeIndex@36", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr MakeIndex([In(), MarshalAs(UnmanagedType.LPStr)] string SourceFile, int IndexMask, int DumpMask, [In(), MarshalAs(UnmanagedType.LPStr)] string AudioFile, bool IgnoreDecodeErrors, IndexCallbackDelegate IP, IntPtr Private, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);//return *FrameIndex

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_ReadIndex@12", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr ReadIndex([In(), MarshalAs(UnmanagedType.LPStr)] string IndexFile, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);//return *FrameIndex

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_ResetOutputFormat@4", CallingConvention = CallingConvention.StdCall)]
        public static extern void ResetOutputFormat(IntPtr pVideoBase);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_SetOutputFormat@24", CallingConvention = CallingConvention.StdCall)]
        public static extern int SetOutputFormat(IntPtr pVideoBase, PixelFormat TargetFormat, int Width, int Height, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_WriteIndex@16", CallingConvention = CallingConvention.StdCall)]
        public static extern int WriteIndex([In(), MarshalAs(UnmanagedType.LPStr)] string IndexFile, IntPtr TrackIndices, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);

        [DllImport("ffms2.dll", EntryPoint = "_FFMS_WriteTimecodes@16", CallingConvention = CallingConvention.StdCall)]
        public static extern int WriteTimecodes(IntPtr pFIV, [In(), MarshalAs(UnmanagedType.LPStr)] string TimecodeFile, [In(), MarshalAs(UnmanagedType.LPStr)] StringBuilder ErrorMsg, uint MsgSize);
    }

    enum TrackType
    {
        FFMS_TYPE_VIDEO = 0,
        FFMS_TYPE_AUDIO = 1,
    };
    enum PixelFormat : int
    {
        FFMS_PIX_FMT_NONE = -1,
        FFMS_PIX_FMT_YUV420P,   ///< Planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
        FFMS_PIX_FMT_YUYV422,   ///< Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
        FFMS_PIX_FMT_RGB24,     ///< Packed RGB 8:8:8, 24bpp, RGBRGB...
        FFMS_PIX_FMT_BGR24,     ///< Packed RGB 8:8:8, 24bpp, BGRBGR...
        FFMS_PIX_FMT_YUV422P,   ///< Planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
        FFMS_PIX_FMT_YUV444P,   ///< Planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
        FFMS_PIX_FMT_RGB32,     ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in cpu endianness
        FFMS_PIX_FMT_YUV410P,   ///< Planar YUV 4:1:0,  9bpp, (1 Cr & Cb sample per 4x4 Y samples)
        FFMS_PIX_FMT_YUV411P,   ///< Planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
        FFMS_PIX_FMT_RGB565,    ///< Packed RGB 5:6:5, 16bpp, (msb)   5R 6G 5B(lsb), in cpu endianness
        FFMS_PIX_FMT_RGB555,    ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in cpu endianness most significant bit to 0
        FFMS_PIX_FMT_GRAY8,     ///<        Y        ,  8bpp
        FFMS_PIX_FMT_MONOWHITE, ///<        Y        ,  1bpp, 0 is white, 1 is black
        FFMS_PIX_FMT_MONOBLACK, ///<        Y        ,  1bpp, 0 is black, 1 is white
        FFMS_PIX_FMT_PAL8,      ///< 8 bit with PIX_FMT_RGB32 palette
        FFMS_PIX_FMT_YUVJ420P,  ///< Planar YUV 4:2:0, 12bpp, full scale (jpeg)
        FFMS_PIX_FMT_YUVJ422P,  ///< Planar YUV 4:2:2, 16bpp, full scale (jpeg)
        FFMS_PIX_FMT_YUVJ444P,  ///< Planar YUV 4:4:4, 24bpp, full scale (jpeg)
        FFMS_PIX_FMT_XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing(xvmc_render.h)
        FFMS_PIX_FMT_XVMC_MPEG2_IDCT,
        FFMS_PIX_FMT_UYVY422,   ///< Packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
        FFMS_PIX_FMT_UYYVYY411, ///< Packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3
        FFMS_PIX_FMT_BGR32,     ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in cpu endianness
        FFMS_PIX_FMT_BGR565,    ///< Packed RGB 5:6:5, 16bpp, (msb)   5B 6G 5R(lsb), in cpu endianness
        FFMS_PIX_FMT_BGR555,    ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in cpu endianness most significant bit to 1
        FFMS_PIX_FMT_BGR8,      ///< Packed RGB 3:3:2,  8bpp, (msb)2B 3G 3R(lsb)
        FFMS_PIX_FMT_BGR4,      ///< Packed RGB 1:2:1,  4bpp, (msb)1B 2G 1R(lsb)
        FFMS_PIX_FMT_BGR4_BYTE, ///< Packed RGB 1:2:1,  8bpp, (msb)1B 2G 1R(lsb)
        FFMS_PIX_FMT_RGB8,      ///< Packed RGB 3:3:2,  8bpp, (msb)2R 3G 3B(lsb)
        FFMS_PIX_FMT_RGB4,      ///< Packed RGB 1:2:1,  4bpp, (msb)2R 3G 3B(lsb)
        FFMS_PIX_FMT_RGB4_BYTE, ///< Packed RGB 1:2:1,  8bpp, (msb)2R 3G 3B(lsb)
        FFMS_PIX_FMT_NV12,      ///< Planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV
        FFMS_PIX_FMT_NV21,      ///< as above, but U and V bytes are swapped

        FFMS_PIX_FMT_RGB32_1,   ///< Packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in cpu endianness
        FFMS_PIX_FMT_BGR32_1,   ///< Packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in cpu endianness

        FFMS_PIX_FMT_GRAY16BE,  ///<        Y        , 16bpp, big-endian
        FFMS_PIX_FMT_GRAY16LE,  ///<        Y        , 16bpp, little-endian
        FFMS_PIX_FMT_YUV440P,   ///< Planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
        FFMS_PIX_FMT_YUVJ440P,  ///< Planar YUV 4:4:0 full scale (jpeg)
        FFMS_PIX_FMT_YUVA420P,  ///< Planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
    };


    [StructLayout(LayoutKind.Sequential)]
    struct AVFrameLite
    {

        ///BYTE*[4]
        public System.IntPtr Data0;
        public System.IntPtr Data1;
        public System.IntPtr Data2;
        public System.IntPtr Data3;

        ///int[4]
        public int Linesize0;
        public int Linesize1;
        public int Linesize2;
        public int Linesize3;

        ///BYTE*[4]
        public System.IntPtr Base0;
        public System.IntPtr Base1;
        public System.IntPtr Base2;
        public System.IntPtr Base3;

        ///int
        public int KeyFrame;

        ///int
        public int PictType;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct VideoProperties
    {

        ///int
        public readonly int Width;

        ///int
        public readonly int Height;

        ///int
        public readonly int FPSDenominator;

        ///int
        public readonly int FPSNumerator;

        ///int
        public readonly int NumFrames;

        ///int
        public readonly PixelFormat PixelFormat;

        ///int
        public readonly int SARNum;

        ///int
        public readonly int SARDen;

        ///int
        public readonly int CropTop;

        ///int
        public readonly int CropBottom;

        ///int
        public readonly int CropLeft;

        ///int
        public readonly int CropRight;

        ///double
        public readonly double FirstTime;

        ///double
        public readonly double LastTime;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct AudioProperties
    {
        public readonly int SampleRate;
        public readonly int Channels;
        public readonly int BitsPerSample;
        public readonly bool Float;
        public readonly long NumSamples;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct TrackTimeBase
    {
        public int Num;
        public int Den;
    }
    class FrameInfo
    {
        public long DTS;
        public long SampleStart;
        public long FilePos;
        public uint FrameSize;
        public bool KeyFrame;

        public FrameInfo(long DTS, bool KeyFrame)
        {
            this.DTS = DTS;
            this.SampleStart = 0;
            this.FilePos = 0;
            this.FrameSize = 0;
            this.KeyFrame = KeyFrame;
        }
        public FrameInfo(long DTS, long SampleStart, bool KeyFrame)
        {
            this.DTS = DTS;
            this.SampleStart = SampleStart;
            this.FilePos = 0;
            this.FrameSize = 0;
            this.KeyFrame = KeyFrame;
        }
        public FrameInfo(long SampleStart, long FilePos, uint FrameSize, bool KeyFrame)
        {
            this.DTS = 0;
            this.SampleStart = SampleStart;
            this.FilePos = FilePos;
            this.FrameSize = FrameSize;
            this.KeyFrame = KeyFrame;
        }


    }

    class FrameBuffer : IDisposable
    {
        Bitmap[] framebitmapbuffer;
        int[] framenumbuffer;
        int num = 0;
        int currentpos = 0;//指示最古老的Bitmap的位置
        
        public FrameBuffer(int n, int width, int height)
        {
            if (n < 1)
            {
                throw new Exception("缓冲区小于1。");
            }
            if (width < 1 || height < 1)
                throw new Exception("指定的图像尺寸非法。");
            framebitmapbuffer = new Bitmap[n];
            framenumbuffer = new int[n];
            for (int i = 0; i < n; i++)
            {
                framebitmapbuffer[i] = new Bitmap(width, height);
                framenumbuffer[i] = -1;
            }
            num = n;
        }
        /// <summary>
        /// 在缓冲区查找指定的帧，如果找到，返回帧图像，否则，返回null
        /// </summary>
        /// <param name="framenum">帧从零开始的索引</param>
        /// <returns></returns>
        public Bitmap Find(int framenum)
        {
            for (int i = 0; i < num; i++)
            {
                if (framenumbuffer[i] == framenum)
                    return framebitmapbuffer[i];
            }
            return null;
        }
        /// <summary>
        /// 取得在缓冲区安放的位置
        /// </summary>
        /// <param name="n">要读取的帧从零开始的索引</param>
        /// <param name="b">位于队列最前端的Bitmap的引用，也是最古老的Bitmap,最古老的将被删除</param>
        public void Enqueue(int n, ref Bitmap b)
        {
            b = framebitmapbuffer[currentpos];
            framenumbuffer[currentpos] = n;
            ++currentpos;
            if (currentpos >= num)//0 1 2 3 4 5 6
                currentpos -= num;
        }

        #region IDisposable 成员

        public void Dispose()
        {
            foreach (Bitmap b in framebitmapbuffer)
                b.Dispose();
        }

        #endregion
    }
    /*
    class ObjectQueue<T> where T : new()
    {
        int num;
        int[] priority;//对象的权,低于0时被抛弃
        List<T> objects;

        public ObjectQueue(int n)
        {
            this.num = n;
            priority = new int[n];
            objects = new List<T>(n);
            for (int i = 0; i < n; i++)
            {
                priority[i] = i;
                objects.Add(new T());
            }


        }
        public void Enqueue(T t)
        {
            int minitem = findmininpriority();
            objects[minitem] = t;
            priority[minitem] = num;
            minusonepriority();
        }
        public T Dequeue()
        {

        }
        private int findmininpriority()
        {
            int output = 0;
            int prioritytemp = num;
            for (int i = 0; i < num; i++)
            {
                if (priority[i] < prioritytemp)
                {
                    output = i;
                    prioritytemp = priority[i];
                }
            }
            return output;
        }
        private int findmaxinpriority()
        {
            int output = num - 1;
            int prioritytemp = 0;
            for (int i = 0; i < num; i++)
            {
                if (priority[i] > prioritytemp)
                {
                    output = i;
                    prioritytemp = priority[i];
                }
            }
            return output;
        }
        private void minusonepriority()
        {
            for (int i = 0; i < num; i++)
            {
                --priority[i];
            }
        }

    }
     */

}
